#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/i2c.h>
#include <linux/wait.h>
#include <linux/input.h>
#include <linux/jiffies.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include "mpu6050_driver.h"

struct mpu6050_driver_dev{
	struct input_dev *pinput;
	struct i2c_client *pclt;
	struct delayed_work work;
};

struct mpu6050_driver_dev *pgmydev;

int mpu6050_read_byte(struct i2c_client *pclt, unsigned char reg)
{
	int ret = 0;
	char txbuf[1] = {reg};
	char rxbuf[1] = {0};
	struct i2c_msg msg[2] = {
		{pclt->addr, 0, 1, txbuf},
		{pclt->addr, I2C_M_RD, 1, rxbuf},
	};
	ret = i2c_transfer(pclt->adapter, msg, ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("i2c_transfer error\n");
		return ret;
	}
	return rxbuf[0];
}

int mpu6050_write_byte(struct i2c_client *pclt, unsigned char reg, unsigned char val)
{
	int ret = 0;
	char txbuf[2] = {reg, val};
	struct i2c_msg msg[1] = {
		{pclt->addr, 0, 2, txbuf},
	};
	ret = i2c_transfer(pclt->adapter, msg, ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("i2c_transfer error\n");
		return ret;
	}
	return 0;
}

void mpu6050_work_func(struct work_struct *pwk)
{
	struct mpu6050_driver_dev *pgmydev = container_of((struct delayed_work*)pwk, struct mpu6050_driver_dev, work);
	unsigned short ax=0, ay=0, az=0, gx=0, gy=0, gz=0, temp=0;
	ax = (mpu6050_read_byte(pgmydev->pclt, MPU_ACCEL_XOUTH_REG)<<8) | mpu6050_read_byte(pgmydev->pclt, MPU_ACCEL_XOUTL_REG);
	ay = (mpu6050_read_byte(pgmydev->pclt, MPU_ACCEL_YOUTH_REG)<<8) | mpu6050_read_byte(pgmydev->pclt, MPU_ACCEL_YOUTL_REG);
	az = (mpu6050_read_byte(pgmydev->pclt, MPU_ACCEL_ZOUTH_REG)<<8) | mpu6050_read_byte(pgmydev->pclt, MPU_ACCEL_ZOUTL_REG);
	gx = (mpu6050_read_byte(pgmydev->pclt, MPU_GYRO_XOUTH_REG)<<8) | mpu6050_read_byte(pgmydev->pclt, MPU_GYRO_XOUTL_REG);
	gy = (mpu6050_read_byte(pgmydev->pclt, MPU_GYRO_YOUTH_REG)<<8) | mpu6050_read_byte(pgmydev->pclt, MPU_GYRO_YOUTL_REG);
	gz = (mpu6050_read_byte(pgmydev->pclt, MPU_GYRO_ZOUTH_REG)<<8) | mpu6050_read_byte(pgmydev->pclt, MPU_GYRO_ZOUTL_REG);
	temp = (mpu6050_read_byte(pgmydev->pclt, MPU_TEMP_OUTH_REG)<<8) | mpu6050_read_byte(pgmydev->pclt, MPU_TEMP_OUTL_REG);

	input_report_abs(pgmydev->pinput, ABS_X, (__s16)ax);
	input_report_abs(pgmydev->pinput, ABS_Y, (__s16)ay);
	input_report_abs(pgmydev->pinput, ABS_Z, (__s16)az);
	input_report_abs(pgmydev->pinput, ABS_RX, (__s16)gx);
	input_report_abs(pgmydev->pinput, ABS_RY, (__s16)gy);
	input_report_abs(pgmydev->pinput, ABS_RZ, (__s16)gz);
	input_report_abs(pgmydev->pinput, ABS_MISC, (__s16)temp);

	input_sync(pgmydev->pinput);

	schedule_delayed_work(&pgmydev->work, msecs_to_jiffies(1000));
}

int mpu6050_init(struct i2c_client *pclt)
{
	mpu6050_write_byte(pclt, MPU_PWR_MGMT1_REG, 0x00);
	mpu6050_write_byte(pclt, MPU_SAMPLE_RATE_REG, 0x07);
	mpu6050_write_byte(pclt, MPU_CFG_REG, 0x06);
	mpu6050_write_byte(pclt, MPU_GYRO_CFG_REG, 0xF8);
	// mpu6050_write_byte(pclt, MPU_ACCEL_CFG_REG, 0x19);
	mpu6050_write_byte(pclt, MPU_ACCEL_CFG_REG, 0x01);

	printk("mpu6050_driver_init finished!\n");
}

int mpu6050_driver_probe(struct i2c_client *pclt, const struct i2c_device_id *pid)
{
	int ret = 0;

	pgmydev = kmalloc(sizeof(struct mpu6050_driver_dev), GFP_KERNEL);
	if(!pgmydev)
	{
		printk("kmalloc failed\n");
		return -1;
	}
	memset(pgmydev, 0, sizeof(struct mpu6050_driver_dev));
	
	pgmydev->pclt = pclt;
	
	mpu6050_init(pgmydev->pclt);

	pgmydev->pinput = input_allocate_device();
	set_bit(EV_ABS, pgmydev->pinput->evbit);
	input_set_abs_params(pgmydev->pinput, ABS_X, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_Y, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_Z, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_RX, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_RY, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_RZ, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_MISC, 0, 65535, 0, 0);

	ret = input_register_device(pgmydev->pinput);
	if(ret)
	{
		printk("input_register_device failed\n");
		input_free_device(pgmydev->pinput);
		pgmydev->pinput = NULL;
		kfree(pgmydev);
		pgmydev = NULL;
		return -1;
	}

	INIT_DELAYED_WORK(&pgmydev->work, mpu6050_work_func);
	schedule_delayed_work(&pgmydev->work, msecs_to_jiffies(1000));

	return 0;
}

int mpu6050_driver_remove(struct i2c_client *pclt)
{
	cancel_delayed_work(&pgmydev->work);
	input_unregister_device(pgmydev->pinput);
	input_free_device(pgmydev->pinput);
	pgmydev->pinput = NULL;
	kfree(pgmydev);
	pgmydev = NULL;
	printk("mpu6050_driver_remove is called\n");
	return 0;
}

struct of_device_id mpu6050_dts[] = {
	{.compatible = "invensense,mpu6050"},
	{}
};

struct i2c_device_id mpu6050_ids[] = {
	[0] = {"mpu6050", 0},
	[1] = {}
};

struct i2c_driver mpu6050_driver = {
	.driver = {
		.name = "mpu6050",
		.owner = THIS_MODULE,
		.of_match_table = mpu6050_dts
	},
	.probe = mpu6050_driver_probe,
	.remove = mpu6050_driver_remove,

	.id_table = mpu6050_ids
};


module_i2c_driver(mpu6050_driver);
MODULE_LICENSE("GPL");

